package util;

import java.awt.Color;
import java.awt.geom.AffineTransform;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Map;
import javax.media.opengl.GL;
import javax.media.opengl.GLContext;
import util.shaders.Program;
import util.texture.TextureDestinationWrapper;
import com.sun.opengl.util.GLUT;
import com.sun.opengl.util.texture.Texture;


public class BasicGraphicsJogl
{
    private static Map<Integer,Integer> indexToSamplerHandleMap = initIndexToSamplerHandleMap();
    private static final int maximumInfoLogSize = 2000;
	private static BasicGraphicsJogl instance;
	public static void createInstance(GLContext glContext)
	{
	    instance = new BasicGraphicsJogl(glContext);
	}
	public static BasicGraphicsJogl instance()
	{
		return instance;
	}
    static int getSamplerHandle(int index)
    {
        initIndexToSamplerHandleMap();
        return indexToSamplerHandleMap.get(index);
    }	
	private final GL gl;
	private final GLUT glut;
	private final GLContext glContext;
	
	private BasicGraphicsJogl(GLContext glContext) 
	{
	    this.glContext = glContext;
	    glContext.makeCurrent();	    
	    this.gl = glContext.getGL();
        glut = new GLUT();
        initGL();
	}
	private void initGL()
	{
     
	}
	
	public void appendTransform(final float[] transform)
	{
		gl.glMultMatrixf(transform,0);
	}


	
	public void drawCenteredImage(Texture texture, Vec2F location,float angle)
	{
		Vec2F halfDims = new Vec2F(texture.getWidth(),texture.getHeight()).scale(0.5f);
		location = location.sub(halfDims);
		BasicGraphicsJogl.instance.bind(texture, 0);
		pushTransform();
		translate(location.getX() ,location.getY());
		rotate(angle,halfDims);
		drawImage(texture);
		popTransform();
	}
	public void drawCenteredImage(Texture texture, Vec2F location)
	{
		drawCenteredImage(texture,location,0);
	}

	
	public void drawImage(Texture texture)
	{
		drawImage(texture,new Vec2F(0,0));
	}

	
	public void drawImage(Texture texture, Vec2F location)
	{
		drawImage(texture,location,new Vec2F(texture.getWidth(),texture.getHeight()));
	}

	public void drawImage(Texture texture, Vec2F location, Vec2F size)
	{
		pushTransform();
		translate(location.getX(),location.getY());
        BasicGraphicsJogl.instance.bind(texture, 0);
		gl.glBegin(GL.GL_QUADS);
		gl.glColor4f(1f, 1f, 1f, 1f);
		gl.glTexCoord2f(0, 0);
		gl.glVertex2f(0,0);
		
		gl.glTexCoord2f(1, 0);
		gl.glVertex2f(size.getX(),0);
		
		gl.glTexCoord2f(1, 1);
		gl.glVertex2f(size.getX(),size.getY());
		
		gl.glTexCoord2f(0, 1);
		gl.glVertex2f(0,size.getY());
		gl.glEnd();		
		popTransform();
	}
	
	public void drawCenteredQuadWithTextureCoords(Vec2F location, Vec2F size,float angle)
	{
		Vec2F halfSize = size.scale(0.5f);
		location = location.sub(halfSize);
		pushTransform();
		translate(location.getX() ,location.getY());
		rotate(angle,halfSize);
		drawQuadWithTextureCoords(size);
		popTransform();
	}
	public void drawCenteredQuadWithTextureCoords(Vec2F location, Vec2F size)
	{
		pushTransform();
		translate(location.getX()-size.getX()/2f,location.getY()-size.getY()/2f);
		drawQuadWithTextureCoords(size);
		popTransform();
	}
	public void drawQuadWithTextureCoords(Vec2F size)
	{
		gl.glBegin(GL.GL_QUADS);
		gl.glTexCoord2f(0, 0);
		gl.glVertex2f(0,0);
		
		gl.glTexCoord2f(1, 0);
		gl.glVertex2f(size.getX(),0);
		
		gl.glTexCoord2f(1, 1);
		gl.glVertex2f(size.getX(),size.getY());
		
		gl.glTexCoord2f(0, 1);
		gl.glVertex2f(0,size.getY());
		gl.glEnd();		
	}

	
	public void drawLine(Vec2F startPoint, Vec2F endPoint, Color color)
	{
		gl.glBegin(GL.GL_LINES);
		gl.glColor3f(color.getRed(), color.getGreen(), color.getBlue());
		gl.glVertex2f(startPoint.getX(),startPoint.getY());
		gl.glVertex2f(endPoint.getX(),endPoint.getY());
		gl.glEnd();
	}

	
	public void drawRect(Vec2F location, Vec2F size, Color4F color)
	{
		pushTransform();
		translate(location);
		gl.glBegin(GL.GL_QUADS);
		gl.glColor4f(color.red, color.green, color.blue, color.alpha);
		gl.glVertex2f(0,0);
		gl.glVertex2f(0,size.getY());
		gl.glVertex2f(size.getX(),size.getY());
		gl.glVertex2f(size.getX(),0);
		gl.glEnd();
		popTransform();
	}
	

	
	public void popClip()
	{
		// TODO Auto-generated method stub
		
	}

	
	public void popTransform()
	{
		gl.glPopMatrix();
		
	}

	
	public void pushClip()
	{
		// TODO Auto-generated method stub
		
	}

	
	public void pushTransform()
	{
		gl.glPushMatrix();
		
	}

	
	public void rotate(float angle, Vec2F center)
	{
		translate(center);
		gl.glRotatef(angle, 0, 0, 1);
		translate(center.scale(-1.0f));
	}
	public void rotate(float angle)
	{
		gl.glRotatef(angle, 0, 0, 1);
	}

	
	public void setAlpha(double transparency)
	{
		// TODO Auto-generated method stub
		
	}

	
	public void setClip(Vec2F Vec2F, Vec2F size)
	{
		
		// TODO Auto-generated method stub
		
	}

	
	public void setTransform(AffineTransform oldTransform)
	{
		// TODO Auto-generated method stub
		
	}

	
	public void translate(Vec2F translation)
	{
		gl.glTranslatef(translation.getX(),translation.getY(),0);
	}
	public void translate(float x, float y)
	{
		gl.glTranslatef(x,y,0);
	}
	/**
	 * An assertion with description of the link/compile error occurs if link/compile failed.
	 * @param shaderHandle Handle to shader to check status of.
	 * @param handleType Either HandleType.Shader or HandleType.Program
	 * @param statusType What to check (GL.GL_COMPILE_STATUS, GL.GL_LINK_STATUS, GL_VALIDATE_STATUS)
	 */
	public void assertShaderStatus(int handle,int statusType)
	{
		BasicGraphicsJogl basicGraphics = BasicGraphicsJogl.instance();
		GL gl = basicGraphics.getGL();
		IntBuffer statusBuffer = IntBuffer.allocate(1);
		int status;
		gl.glGetObjectParameterivARB(handle, statusType, statusBuffer);
		status = statusBuffer.get(0);
		if(status != GL.GL_TRUE)
		{
        	IntBuffer lengthBuffer = IntBuffer.allocate(1);
        	ByteBuffer infoLog = ByteBuffer.allocate(maximumInfoLogSize);
        	String infoLogAsString = "";
           	gl.glGetInfoLogARB(handle, maximumInfoLogSize , lengthBuffer, infoLog);
    		byte[] logInfoBytes = infoLog.array();
    		try
			{
				infoLogAsString = new String(logInfoBytes, "UTF8");
			} 
    		catch (UnsupportedEncodingException e)
			{
            	assert false : "Shader compile error ID = " + String.valueOf(handle) +  ".  Can't display error message because can't convert to UTF - 8";
			}

        	assert false : "Shader compile error ID = " + String.valueOf(handle) +  ".  Message = \n"  + infoLogAsString;
		}
	}

	public void setShaderProgram(Program program)
	{
		if(program == null)
		{
			gl.glUseProgramObjectARB(0);
		}
		else
		{
			program.set();
		}
	}

	public void setRenderTarget(TextureDestinationWrapper destination)
	{
		destination.setRenderTarget();
	}
	public void clearRenderTarget()
	{
		TextureDestinationWrapper.clearRenderTarget();
	}
	public void switchRenderTarget(TextureDestinationWrapper destination)
	{
		destination.switchRenderTarget();
	}
	
	public void clearTexture(int textureChannel)
	{
		gl.glActiveTexture(textureChannel);
		gl.glBindTexture(GL.GL_TEXTURE_2D, 0);
	}
    public void bind(Texture texture, int samplerIndex)
    {
        int samplerHandle = getSamplerHandle(samplerIndex);
        gl.glActiveTexture(samplerHandle);
        gl.glBindTexture(GL.GL_TEXTURE_2D,texture.getTextureObject());
    }
    

    static Map<Integer, Integer> initIndexToSamplerHandleMap()
    {
        Map<Integer,Integer> indexToSamplerHandleMap = new HashMap<Integer,Integer>(200);      
        indexToSamplerHandleMap = new HashMap<Integer,Integer>(200);        
        indexToSamplerHandleMap.put(0, GL.GL_TEXTURE0);
        indexToSamplerHandleMap.put(1, GL.GL_TEXTURE1);
        indexToSamplerHandleMap.put(2, GL.GL_TEXTURE2);
        indexToSamplerHandleMap.put(3, GL.GL_TEXTURE3);
        indexToSamplerHandleMap.put(4, GL.GL_TEXTURE4);
        indexToSamplerHandleMap.put(5, GL.GL_TEXTURE5);
        indexToSamplerHandleMap.put(6, GL.GL_TEXTURE6);
        indexToSamplerHandleMap.put(7, GL.GL_TEXTURE7);
        indexToSamplerHandleMap.put(8, GL.GL_TEXTURE8);
        indexToSamplerHandleMap.put(9, GL.GL_TEXTURE9);
        indexToSamplerHandleMap.put(10, GL.GL_TEXTURE10);
        indexToSamplerHandleMap.put(11, GL.GL_TEXTURE11);
        indexToSamplerHandleMap.put(12, GL.GL_TEXTURE12);
        indexToSamplerHandleMap.put(13, GL.GL_TEXTURE13);
        indexToSamplerHandleMap.put(14, GL.GL_TEXTURE14);
        indexToSamplerHandleMap.put(15, GL.GL_TEXTURE15);
        return indexToSamplerHandleMap;
    }    
    public GL getGL()
    {
        return gl;
    }

    public GLUT getGlut()
    {
        return glut;
    }
    public GLContext getContext()
    {
        return glContext;
    }

}
